#ifndef _B5M_LOGGER_H_
#define _B5M_LOGGER_H_

#include "common.h"
#include <fstream>
#include <ostream>
#include <string>
#include <sstream>
#include <ctime>
#include "B5M_Thread_Mutex.h"
#include "B5M_Singleton.h"
#include "B5M_Guard.h"

B5M_BEGIN_VERSIONED_NAMESPACE_DECL

/// Comment this line if you don't need multithread support
//#define LOGGER_MULTITHREAD

#define LOGGER_TRACK

const int DBG_ERROR	= 0;
const int DBG_WARN	= 1;
const int DBG_INFO	= 2;
const int DBG_DEBUG	= 3;
const int DBG_CARE = DBG_WARN;


#ifdef LOGGER_MULTITHREAD
	#include <pthread.h>
#endif

/**
 * \brief Macro to configure the logger.
 * Example of configuration of the Logger:
 * 	m_strPswdKey("outputfile", Logger::file_on|Logger::screen_on, DBG_DEBUG, DBG_ERROR);
 */
#define LOGGER_CONFIG(outputFile, \
		configuration, \
		fileVerbosityLevel, \
		screenVerbosityLevel, \
		outputVerbosityLevel) { \
			sgtLogger::instance()->configure(outputFile, \
						configuration, \
						fileVerbosityLevel, \
						screenVerbosityLevel, \
						outputVerbosityLevel); \
		}



/**
 * \brief Simple logger to log messages on file and console.
 * This is the implementation of a simple logger in C++. It is implemented 
 * as a Singleton, so it can be easily called through two DEBUG macros.
 * It is Pthread-safe.
 * It allows to log on both file and screen, and to specify a verbosity
 * threshold for both of them.
 */
class B5M_Logger
{
	/**
	 * \brief Type used for the configuration
	 */
	enum loggerConf_ {
		L_nofile_	= 	1 << 0,
		L_file_		=	1 << 1,
		L_noscreen_	=	1 << 2,
		L_screen_	=	1 << 3,
		L_nooutput_ =	1 << 4,
		L_output_	=	1 << 5
	};

	bool configured_;

	B5M_Thread_Mutex mutex_;
	/**
	 * \brief Initial part of the name of the file used for Logging.
	 * Date and time are automatically appended.
	 */
	std::string logFile_;

	/**
	 * \brief Current configuration of the logger.
	 * Variable to know if logging on file and on screen are enabled.
	 * Note that if the log on file is enabled, it means that the
	 * logger has been already configured, therefore the stream is
	 * already open.
	 */
	loggerConf_ configuration_;

	/**
	 * \brief Stream used when logging on a file
	 */
	std::ofstream out_;

	/**
	 * \brief Initial time (used to print relative times)
	 */
	time_t initialTime_;

	/**
	 * \brief Verbosity threshold for files
	 */
	unsigned int fileVerbosityLevel_;

	/**
	 * \brief Verbosity threshold for screen
	 */
	unsigned int screenVerbosityLevel_;

	unsigned int outputVerbosityLevel_;
public:
	B5M_Logger();

	~B5M_Logger();

public:

	typedef loggerConf_ loggerConf;
	static const loggerConf file_on= 	L_nofile_;
	static const loggerConf file_off= 	L_file_;
	static const loggerConf screen_on= 	L_noscreen_;
	static const loggerConf screen_off= L_screen_;
	static const loggerConf output_on= 	L_nooutput_;
	static const loggerConf output_off=	L_output_;


	void print(const unsigned int		verbosityLevel,
			   const std::string&		sourceFile,
			   const int 				codeLine,
			   const std::string&		function,
			   const std::string& 		message);



	void print(const unsigned int		verbosityLevel,
			   const std::string&		file,
			   const int				line,
			   const std::string&		function,
			   const std::wstring&		message);

	void configure (const std::string&	outputFile,
				    const loggerConf	configuration,
			        const int			fileVerbosityLevel,
			        const int			screenVerbosityLevel,
					const int			outputVerbosityLevel);

	B5M_NONE_COPYABLE(B5M_Logger);
};

#define sgtLogger B5M_Singleton<B5M_Logger>

inline B5M_Logger::loggerConf operator|
	(B5M_Logger::loggerConf __a, B5M_Logger::loggerConf __b)
{
	return B5M_Logger::loggerConf(static_cast<int>(__a) |
		static_cast<int>(__b));
}

inline B5M_Logger::loggerConf operator&
	(B5M_Logger::loggerConf __a, B5M_Logger::loggerConf __b)
{
	return B5M_Logger::loggerConf(static_cast<int>(__a) &
		static_cast<int>(__b)); }


/**
 * \brief Macro to print log messages.
 * Example of usage of the Logger:
 *	    DEBUG(DBG_DEBUG, "hello " << "world");
 */
#define DEBUGA(priority, msg) { \
	std::ostringstream __debug_stream__; \
	__debug_stream__ << msg; \
	sgtLogger::instance()->print(priority, __FILE__, __LINE__, __FUNCTION__, \
			__debug_stream__.str()); \
	}

#define DEBUGW(priority, msg) { \
	std::wostringstream __debug_stream__; \
	__debug_stream__ << msg; \
	sgtLogger::instance()->print(priority, __FILE__, __LINE__, __FUNCTION__, \
	__debug_stream__.str()); \
}

#define __TRACK__ { \
	const DWORD last_error = GetLastError(); \
	std::ostringstream __debug_stream__; \
	__debug_stream__ << "*_*$" << last_error; \
	sgtLogger::instance()->print(DBG_DEBUG, __FILE__, __LINE__, __FUNCTION__, \
	__debug_stream__.str()); \
}

B5M_END_VERSIONED_NAMESPACE_DECL

#endif /* _B5M_LOGGER_H_ */
